/*
 * utils.c
 *
 * Copyright (C) 2011 - Dr.NP
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @version utils.c 1.0.0
 * @package libbspcore
 * @author Dr.NP <np@bsgroup.org>
 * @update 03/21/2011
 */

/**
 * Runtime utils
 *
 * === CHANGELOG ===
 * [03/21/2011] - Creation
 * [08/09/2011] - realpath buffer size bug fixed
 */

#define _GNU_SOURCE

#include "bsp.h"

#include "bsp_utils.h"
#include "bsp_socket.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <err.h>
#include <error.h>
#include <math.h>
#include <limits.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/resource.h>

// FD list
struct fd_info_t *fd_list = NULL;
size_t fd_list_size;
int fd_total;

struct abuff_t **free_abuff_list = NULL;
size_t free_abuff_list_size;
int free_abuff_total;

/**
 * Set a file descriptor to non-blocking mode
 *
 * @param int fd                                File descriptor
 *
 * @return int                                  Return value
 */
int set_nonblock(int fd)
{
    int flags;

    flags = fcntl(fd, F_GETFL);
    if (flags < 0)
    {
        return flags;
    }

    flags |= O_NONBLOCK;
    if (fcntl(fd, F_SETFL, flags) < 0)
    {
        return RTN_ERROR_FATAL;
    }

    return RTN_SUCCESS;
}

/**
 * Get current maxnium file descriptors for open
 * 
 * @return int                                  Number of fds
 */
int get_max_fds()
{
    struct rlimit rlim;
    
    if (0 == getrlimit(RLIMIT_NOFILE, &rlim))
    {
        return rlim.rlim_cur;
    }
    
    return RTN_ERROR_FATAL;
}
/**
 * Set rlimit resource
 * Maxnium fds for current process
 *
 * @return int                                  Return value
 */
int set_rlimit()
{
    struct rlimit rlim, rlim_new;
    
    if (0 == getrlimit(RLIMIT_CORE, &rlim))
    {
        // Increase RLIMIT_CORE to infinity if possible
        rlim_new.rlim_cur = rlim_new.rlim_max = RLIM_INFINITY;

        if (0 != setrlimit(RLIMIT_CORE, &rlim_new))
        {
            // Set rlimit error
            rlim_new.rlim_cur = rlim_new.rlim_max = rlim.rlim_max;
            (void) setrlimit(RLIMIT_CORE, &rlim_new);
        }
    }

    if (0 != getrlimit(RLIMIT_CORE, &rlim) || rlim.rlim_cur == 0)
    {
        err(RTN_ERROR_RESOURCE_GET, "Get rlimit.CORE error");
    }

    // Read current RLIMIT_NOFILE
    if (0 != getrlimit(RLIMIT_NOFILE, &rlim))
    {
        err(RTN_ERROR_RESOURCE_GET, "Get rlimit.NOFILE error");
    }

    else
    {
        // Enlarge RLIMIT_NOFILE to allow as many connections as we need
        int maxfiles = MAX_FILES;

        if (rlim.rlim_cur < maxfiles)
        {
            rlim.rlim_cur = maxfiles;
        }

        if (rlim.rlim_max < rlim.rlim_cur)
        {
            if (0 == getuid() || 0 == geteuid())
            {
                // You are root?
                rlim.rlim_max = rlim.rlim_cur;
            }

            else
            {
                // You are not root?
                debug_info("You cannot set NOFILE to %d, check your superuser privilege\n", (int) rlim.rlim_cur);
                rlim.rlim_cur = rlim.rlim_max;
            }
        }

        if (0 != setrlimit(RLIMIT_NOFILE, &rlim))
        {
            // Root needed
            err(RTN_ERROR_RESOURCE_SET, "Set rlimit error, check your superuser privilege");
        }
    }
    
    return rlim.rlim_max;
}

/**
 * Get current process' directory (Real path)
 *
 * @return string                               Directory name
 */
char * get_dir()
{
    char self_name[_POSIX_PATH_MAX];
    char *ret = NULL;
    char *curr;

    if (-1 == readlink("/proc/self/exe", self_name, _POSIX_PATH_MAX))
    {
        ret = "./";
    }

    else
    {
        ret = realpath(self_name, NULL);
        curr = strrchr(ret, '/');

        if (curr)
        {
            curr[0] = 0x0;
        }
    }

    return ret;
}

/**
 * Write a short to first 2 bytes
 *
 * @param uint val                              Value to put
 * @param string src                            Target string
 */
inline void put_u_short(char *src, u_int16_t val)
{
    if (src)
    {
        src[0] = val >> 8;
        src[1] = val % 0x100;
    }

    return;
}

/**
 * Get a short from input
 *
 * @param string src                            Target string
 *
 * @return uint                                 Return value
 */
inline u_int16_t get_u_short(char *src)
{
    u_int16_t val = 0;

    if (src)
    {
        val = (((u_int8_t) src[0]) << 8) + 
            ((u_int8_t) src[1]);
    }

    return val;
}

/**
 * Write an unsigned int to first 4 bytes (BIG_ENDIAN)
 *
 * @param uint val                              Value to put
 * @param string src                            Target string
 */
inline void put_u_int(char *src, u_int32_t val)
{
    if (src)
    {
        src[0] = val >> 24;
        src[1] = (val >> 16) % 0x100;
        src[2] = (val >> 8) % 0x100;
        src[3] = val % 0x100;
    }
    
    return;
}

/**
 * Get an unsigned int from input
 *
 * @param string src                            Target string
 *
 * @return uint                                 Return value
 */
inline u_int32_t get_u_int(char *src)
{
    u_int32_t val = 0;
    
    if (src)
    {
        val = (((u_int8_t) src[0]) << 24) + 
            (((u_int8_t) src[1]) << 16) + 
            (((u_int8_t) src[2]) << 8) + 
            ((u_int8_t) src[3]);
    }
    
    return val;
}

/**
 * Write an unsigned long long to first 8 bytes (BIG_ENDIAN)
 *
 * @param uint val                              Value to put
 * @param string src                            Target string
 */
inline void put_u_ll(char *src, u_int64_t val)
{
    if (src)
    {
        src[0] = val >> 56;
        src[1] = (val >> 48) % 0x100;
        src[2] = (val >> 40) % 0x100;
        src[3] = (val >> 32) % 0x100;
        src[4] = (val >> 24) % 0x100;
        src[5] = (val >> 16) % 0x100;
        src[6] = (val >> 8) % 0x100;
        src[7] = val % 0x100;
    }

    return;
}

/**
 * Get an unsigned long long from input
 *
 * @param string src                            Target string
 *
 * @return uint                                 Return value
 */
inline u_int64_t get_u_ll(char *src)
{
    u_int64_t val = 0;

    if (src)
    {
        val = ((u_int64_t) ((((u_int8_t) src[0]) << 24) + 
            (((u_int8_t) src[1]) << 16) + 
            (((u_int8_t) src[2]) << 8) + 
            (((u_int8_t) src[3]))) << 32) + 
            (((u_int8_t) src[4]) << 24) + 
            (((u_int8_t) src[5]) << 16) + 
            (((u_int8_t) src[6]) << 8) +
            ((u_int8_t) src[7]);
    }

    return val;
}

/**
 * Similar to strtoll function
 * Parse a string into a long integer(10base)
 *
 * @param string input                          Input string
 * @param long long int                         Target value address
 */
inline void parse_ll(const char *input, long long int *value)
{
    int i = 0;
    long long int nv = 1;
    char curr = input[0];
    *value = 0;

    while ((curr <= 57 && curr >= 48) || (0 == i && 45 == curr))
    {
        if (45 == curr)
        {
            // Nagetive
            nv = -1;
        }

        else
        {
            // Digital
            //*value = *value << 4;
            *value *= 10;
            *value += curr - 48;
        }
        
        curr = input[++ i];
    }

    if (curr != 0)
    {
        *value = 0;
    }

    *value *= nv;
    
    return;
}

/**
 * Generate an unique id
 * POSIX-style OS only
 *
 * @param string input                          Buffer to store the result
 * @param int len                               Result length
 */
void gen_unique_id(char *input, int len)
{
    static int fd = -2;
    unsigned int seed = 0;
    struct timeval tm;

    if (fd == -2)
    {
        fd = open("/dev/urandom", O_RDONLY|O_NONBLOCK);

        // urandom service is more efficient
        if (fd == -1)
        {
            fd = open("/dev/random", O_RDONLY|O_NONBLOCK);
        }
    }

    if (fd < 0)
    {
        return;
    }

    int i, nbytes = len;

    while (nbytes > 0)
    {
        i = read(fd, input + (len - nbytes), nbytes);
        if (i <= 0)
        {
            continue;
        }

        nbytes -= i;
    }

    // We need random seed here
    gettimeofday(&tm, NULL);
    seed = (getpid() << 0x10) ^ getuid() ^ tm.tv_sec ^ tm.tv_usec;

    for (i = 0; i < len ; i ++)
    {
        input[i] = rand_r(&seed) & 0xFF;
    }
    
    return;
}

/**
 * Application terminate signal handler
 *
 * @param int sig                               Signal to handle
 */
void sig_handler(const int sig)
{
    fprintf(stderr, "\nSIGINT %d handled, core terminated\n", sig);
    
    exit(RTN_SUCCESS);

    return;
}

/**
 * Set signal handler
 */
void set_sig(void (* func)(int))
{
    struct sigaction *sa = (struct sigaction *) malloc(sizeof(struct sigaction));
    
    // Process killed by user?
    if (!func)
    {
        func = SIG_DFL;
    }
    
    signal(SIGINT, func);
    signal(SIGTERM, func);
    signal(SIGQUIT, func);
    signal(SIGKILL, func);

    // Ignore SIGPIPE & SIGCLD
    sa->sa_handler = SIG_IGN;
    sa->sa_flags = 0;
    signal(SIGPIPE, SIG_IGN);
    if (sigemptyset(&sa->sa_mask) == -1 || sigaction(SIGPIPE, sa, 0) == -1)
    {
        fprintf(stderr, "Ignore SIGPIPE failed\n");
        exit(RTN_ERROR_SIGNAL);
    }

    return;
}

/**
 * Process daemonize
 *
 * @return int                                  Return value
 */
int daemonize()
{
    int fd;

    // Fork a new process
    switch(fork())
    {
        case -1 :
            return -1;
        case 0 :
            break;
        default :
            exit(1);
    }
    
    if (setsid() == -1)
    {
        return -1;
    }

    // Redirect standard IO to null device
    fd = open("/dev/null", O_RDWR, 0);
    if (fd)
    {
        (void) dup2(fd, STDIN_FILENO);
        (void) dup2(fd, STDOUT_FILENO);
        (void) dup2(fd, STDERR_FILENO);
        if (fd > STDERR_FILENO)
        {
            (void) close(fd);
        }
    }
    
    return 0;
}

/**
 * Output a debug info
 *
 * @param string info                           Output string
 */
void debug_info(const char *fmt, ...)
{
#ifdef DEBUG_INFO
    va_list ap;

    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    va_end(ap);
#endif
    return;
}

/**
 * Debug hex data
 *
 * @param void data                             Input data
 * @param int len                               Length of data
 */
void debug_hex_data(const char *data, int len)
{
    int i;
    
    fprintf(stderr, "\n================ DEBUG HEX INFO [%d bytes] ================\n", len);
    for (i = 0; i < len; i ++)
    {
        fprintf(stderr, "%02X ", (unsigned char) data[i]);
        if (i % 16 == 15)
        {
            fprintf(stderr, "\n");
        }
        
        else if (i % 8 == 7)
        {
            fprintf(stderr, "  ");
        }
    }
    
    fprintf(stderr, "\n===================== END OF HEX INFO =====================\n");
    for (i = 0; i < len; i ++)
    {
        if (data[i] >= 32 && data[i] <= 127)
        {
            fprintf(stderr, "%c", data[i]);
        }

        else
        {
            fprintf(stderr, ".");
        }
    }

    fprintf(stderr, "\n\n");
    
    return;
}

/**
 * Replace sub string
 *
 * @param string input                          Input string
 * @param string search                         The value searched for
 * @param string replace                        The replacement value
 *
 * @return string                               Output string
 */
char * str_replace(const char *input, const char *search, const char *replace)
{
    char *pi, *po, *p, *ret;
    int leni = strlen(input);
    int leno = 1 << (int) ceil(log2(leni));
    int lens = strlen(search);
    int lenr = strlen(replace);
    int len, pos;
    pi = (char *) input;
    po = (char *) malloc(leno);

    if (!po)
    {
        return NULL;
    }

    ret = po;
    pos = 0;
    p = strstr(input, search);

    if (p)
    {
        while (p)
        {
            len = (int) (p - pi);
            while (leno < pos + len + lenr)
            {
                ret = realloc(ret, 2 * leno);

                if (!ret)
                {
                    return NULL;
                }

                leno *= 2;
                po = ret + pos;
            }
            
            memcpy(po, pi, len);
            memcpy(po + len, replace, lenr);

            pi = p + lens;
            po = po + len + lenr;
            pos = pos + len + lenr;

            p = strstr(pi, search);
        }

        strcpy(po, pi);
    }

    else
    {
        strcpy(po, pi);
    }

    return ret;
}

/**
 * Replace sub string, non-allocate version
 *
 * @param string input                          Input string
 * @param string output                         Result buffer
 * @param int leno                              Output buffer size
 * @param string search                         The value searched for
 * @param string replace                        The replacement value
 */
void str_replace2(const char *input, char *output, int leno, const char *search, const char *replace)
{
    char *pi, *po, *p;
    int lens = strlen(search);
    int lenr = strlen(replace);
    int len, pos;
    pi = (char *) input;
    po = output;

    if (!po)
    {
        return;
    }

    pos = 0;
    p = strstr(input, search);

    if (p)
    {
        while (p)
        {
            len = (int) (p - pi);
            if (leno < pos + len + lenr)
            {
                return;
            }
            
            memcpy(po, pi, len);
            memcpy(po + len, replace, lenr);

            pi = p + lens;
            po = po + len + lenr;
            pos = pos + len + lenr;

            p = strstr(pi, search);
        }

        strcpy(po, pi);
    }

    else
    {
        strcpy(po, pi);
    }
    
    return;
}

/**
 * Trim spaces from string
 * NOTE: Source data will be modified after trim
 * 
 * @param string                                Input
 * @param int                                   Trim type (TRIM_TYPE_ALL / TRIM_TYPE_LEFT / TRIM_TYPE_RIGHT)
 *
 * @return int                                  Spaces count
 */
int str_trim(char *input, char trim_type)
{
    int spaces = 0;
    int i;
    size_t curr_len = 0;
    unsigned char curr_code;

    if (!input)
    {
        return 0;
    }

    curr_len = strlen(input);
    if (TRIM_TYPE_ALL == trim_type || TRIM_TYPE_LEFT == trim_type)
    {
        // Trim left spaces
        for (i = 0; i < curr_len; i ++)
        {
            curr_code = (unsigned char) input[i];

            if (curr_code > 0x20)
            {
                // Space breaks
                break;
            }
        }

        if (i > 0)
        {
            memmove(input, input + i, curr_len - i);
            spaces += i;
            input[curr_len - i] = 0x0;
        }
    }

    curr_len = strlen(input);
    if (TRIM_TYPE_ALL == trim_type || TRIM_TYPE_RIGHT == trim_type)
    {
        // Trim right spaces
        for (i = curr_len - 1; i >= 0; i --)
        {
            curr_code = (unsigned char) input[i];

            if (curr_code > 0x20)
            {
                break;
            }
        }

        if (i > 0)
        {
            spaces += i;
            input[curr_len - i] = 0x0;
        }
    }

    return spaces;
}

/**
 * strncmp, ignore empty charactors
 * 
 * @param string                                String 1
 * @param string                                String 2
 * @param integer                               Compare length
 * 
 * @return integer                              0 / 1 / -1 (Equal / s1 great than s2 / s2 great then s1)
 */
int strtrimncmp(const char *s1, const char *s2, size_t n, int ignore_case)
{
    int n1 = 0;
    int n2 = 0;
    int i;

    for (i = 0; i < strlen(s1); i ++)
    {
        if (s1[i] > 0x20)
        {
            break;
        }

        else
        {
            n1 ++;
        }
    }

    for (i = 0; i < strlen(s2); i ++)
    {
        if (s2[i] > 0x20)
        {
            break;
        }

        else
        {
            n2 ++;
        }
    }

    return ignore_case ? strncasecmp(s1 + n1, s2 + n1, n) : strncmp(s1 + n1, s2 + n2, n);
}

/**
 * Load a file into memory
 * Return buffer can be free immediatly
 *
 * @param string                                Filename
 *
 * @return string                               Buffer pointer
 */
char *load_file(const char *filename, int *l)
{
    int size;
    int fp = open(filename, O_RDONLY);
    struct stat s;

    if (!fp)
    {
        return NULL;
    }

    fstat(fp, &s);
    size = (int) s.st_size;
    *l = size;

    char *ret = malloc(size + 1);
    if (!ret)
    {
        return NULL;
    }
    
    read(fp, ret, size);
    close(fp);
    ret[size] = 0;

    return ret;
}

/**
 * Init abuff list
 *
 * @return int                                  Return value
 */
int abuff_init()
{
    if (free_abuff_list)
    {
        return RTN_SUCCESS;
    }
    
    free_abuff_list = ensure_list_space(NULL, sizeof(struct abuff_t *), &free_abuff_list_size, ABUFF_LIST_INITIAL);
    free_abuff_total = 0;

    if (!free_abuff_list)
    {
        err(RTN_ERROR_MEMORY_ALLOC, "Abuff list error!!!");
    }
    
    return RTN_SUCCESS;
}

/**
 * Create a new buffer (abuff)
 *
 * @return abuff                                New abuff pointor
 */
struct abuff_t * new_abuff()
{
    if (!free_abuff_list)
    {
        abuff_init();
    }

    struct abuff_t *ret;

    if (free_abuff_total)
    {
        ret = free_abuff_list[-- free_abuff_total];
    }

    else
    {
        ret = malloc(sizeof(struct abuff_t));

        if (!ret)
        {
            return NULL;
        }

        char *buff = malloc(ABUFF_INITIAL);
        if (!buff)
        {
            free(ret);
            return NULL;
        }

        ret->buff_size = ABUFF_INITIAL;
        ret->buff = buff;
    }

    ret->buff[0] = 0;
    ret->data_size = 0;

    return ret;
}

/**
 * Clean buffer data
 *
 * @param abuff b                               Abuff object to clean
 */
void clean_abuff(struct abuff_t *b)
{
    char *buff;
    
    if (!b)
    {
        return;
    }

    if (!b->buff)
    {
        buff = malloc(ABUFF_INITIAL);
        if (!buff)
        {
            return;
        }
        
        b->buff_size = ABUFF_INITIAL;
    }

    b->buff[0] = 0;
    b->data_size = 0;

    return;
}

/**
 * Destroy an abuff
 *
 * @param abuff b                               Abuff object to free
 */
void free_abuff(struct abuff_t *b)
{
    if (!b || !free_abuff_list)
    {
        return;
    }

    while (free_abuff_total >= free_abuff_list_size)
    {
        ensure_list_space(free_abuff_list, sizeof(struct abuff_t *), &free_abuff_list_size, 2 * free_abuff_list_size);
        if (!free_abuff_list)
        {
            err(RTN_ERROR_MEMORY_ALLOC, "Abuff list error!!!");
        }
    }

    free_abuff_list[free_abuff_total ++] = b;

    return;
}

/**
 * Append orignal data to abuff
 *
 * @param abuff b                               Abuff object to append
 * @param string data                           Data to append
 * @param int len                               Data length
 *
 * @return int                                  Length appended
 */
int append_abuff(struct abuff_t *b, char *data, size_t len, int times)
{
    int new_size, i;
    char *new_buff;
    
    if (!b || !b->buff)
    {
        return RTN_ERROR_FATAL;
    }

    if ((signed) len < 0)
    {
        len = strlen(data);
    }
    
    for (i = 0; i < times; i ++)
    {
        if (b->data_size + len > b->buff_size)
        {
            new_size = 1 << (int) ceil(log2(b->data_size + len));
            new_buff = realloc(b->buff, new_size);
            if (!new_buff)
            {
                return RTN_ERROR_FATAL;
            }

            b->buff_size = new_size;
            b->buff = new_buff;
        }

        memcpy(b->buff + b->data_size, data, len);
        b->data_size += len;
    }

    return len;
}

/**
 * Append formatted data to abuff
 *
 * @param abuff b                               Abuff object to append
 * @param string fmt                            Stream format
 * @param args ... arg                          formatted data
 *
 * @return int                                  Length appended
 */
int printf_abuff(struct abuff_t *b, char *fmt, ...)
{
    char *tmp = NULL;
    int ret = 0;
    
    va_list ap;
    va_start(ap, fmt);
    vasprintf(&tmp, fmt, ap);
    va_end(ap);

    if (!tmp)
    {
        return ret;
    }

    ret = append_abuff(b, tmp, strlen(tmp) + 1, 1);
    b->data_size --;
    
    free(tmp);

    return ret;
}

/**
 * Load file into abuff, all data in abuff will be removed
 *
 * @param string filename                       File to load
 * @param abuff b                               Abuff object
 *
 * @return int                                  Length loaded
 */
int load_file_abuff(struct abuff_t *b, const char *filename)
{
    int size = 0;
    
    if (!b)
    {
        return RTN_ERROR_FATAL;
    }
    
    char *tmp = load_file(filename, &size);
    if (tmp)
    {
        clean_abuff(b);
        append_abuff(b, tmp, size + 1, 1);
        free(tmp);
        tmp = NULL;

        return size;
    }

    else
    {
        return RTN_ERROR_FATAL;
    }
}

/**
 * String replacement - abuff version, source content will be break
 *
 * @param abuff b                               Abuff object
 * @param string search                         The value search for
 * @param string replace                        The replacement value
 */
void str_replace_abuff(struct abuff_t *b, const char *search, const char *replace)
{
    if (!b || !b->buff)
    {
        return;
    }

    char *p = strstr(b->buff, search);
    char *curr = b->buff;
    int new_size;
    char *new_buff;
    int lens = strlen(search);
    int lenr = strlen(replace);
    
    if (!p)
    {
        // Nothing replace
        return;
    }

    while (p)
    {
        if (b->data_size - lens + lenr > b->buff_size)
        {
            // Enlarge abuff
            new_size = 1 << (int) ceil(log2(b->data_size - lens + lenr));
            new_buff = realloc(b->buff, new_size);

            if (!new_buff)
            {
                return;
            }

            curr = new_buff + (curr - b->buff);
            p = new_buff + (p - b->buff);
            b->buff = new_buff;
            b->buff_size = new_size;
        }

        if (lens != lenr)
        {
            memmove(p + lenr, p + lens, b->data_size - lens - (p - b->buff));
        }
        
        memcpy(p, replace, lenr);

        curr = p + lenr;
        p = strstr(curr, search);
        b->data_size = strlen(b->buff) + 1;
    }
    
    return;
}

/**
 * Output abuff content as string
 *
 * @param abuff b                               Abuff object
 *
 * @return int                                  Data length
 */
int debug_abuff(struct abuff_t *b)
{
    if (!b || !b->buff)
    {
        return RTN_ERROR_FATAL;
    }
    
    fprintf(stderr, "\n======== ABUFF CONTENT ========\n\n");
    fprintf(stderr, "%s", b->buff);
    fprintf(stderr, "\n======== ABUFF %d bytes ========\n", (int) b->data_size);

    return b->data_size;
}

/**
 * Ensure pool(list) space
 *
 * @param void *                                Pointor to old list address, if NULL, an new list will be created
 * @param size_t                                Item size
 * @param size_t *                              Current size, 0 for new pool creation
 * @param size_t                                Target_size
 *
 * @return void *                               New pool list(if available)
 */
void * ensure_list_space(void *list, size_t item_size, size_t *curr_size, size_t new_size)
{
    void *tmp;
    size_t raw_size;
    
    if (new_size <= 0 || item_size <= 0 || (curr_size && new_size == *curr_size))
    {
        // What are you doing? free me?
        return list;
    }

    raw_size = new_size * item_size;
    
    if (!list)
    {
        // Create a new list
        tmp = malloc(raw_size);
        if (!tmp)
        {
            return NULL;
        }

        memset(tmp, 0, raw_size);

        if (curr_size)
        {
            *curr_size = new_size;
        }

        //debug_info("A new list created as size %d\n", new_size);
    }

    else
    {
        if (!curr_size)
        {
            return list;
        }
        
        // Realloc
        tmp = realloc(list, raw_size);
        if (!tmp)
        {
            return list;
        }
        
        //debug_info("List realloc from %d to %d\n", *curr_size, new_size);
        *curr_size = new_size;
    }

    return tmp;
}

/**
 * File descriptor list initial
 *
 * @param int size                              List size
 *
 * @return int                                  Return value
 */
int fdset_init(size_t size)
{
    int res;

    fd_list = ensure_list_space(NULL, sizeof(struct fd_info_t), &fd_list_size, size);
    if (!fd_list)
    {
        err(RTN_ERROR_MEMORY_ALLOC, "Cannot initialze file descriptor list");
    }

    else
    {
        res = RTN_SUCCESS;
        fd_total = size;
        debug_info("File descriptor list initialzed as %d\n", size);
    }
    
    return res;
}

/**
 * Add a file descriptor information
 *
 * @param int fd                                File descriptor
 * @param int id                                Server / Client / Connector ID
 * @param char fd_type                          FD_TYPE_SERVER / FD_TYPE_CLIENT / FD_TYPE_CONNECTOR
 *
 * @return int                                  Return value
 */
int set_fd(int fd, int id, char fd_type)
{
    if (!fd_list)
    {
        // uninitialized
        return RTN_ERROR_GENERAL;
    }
    
    while (fd >= fd_list_size)
    {
        // Just enlarge
        fd_list = ensure_list_space(fd_list, sizeof(struct fd_info_t), &fd_list_size, fd_list_size * 2);
    }

    fd_list[fd].id = id;
    fd_list[fd].type = fd_type;

    //debug_info("File descriptor %d set as %d => %d\n", fd, fd_type, id);

    return RTN_SUCCESS;
}

/**
 * Get a file descriptor's information
 *
 * @param int fd                                File descriptor
 *
 * @return fd_info_t                            FD information
 */
struct fd_info_t * get_fd(int fd)
{
    struct fd_info_t *ret = NULL;

    if (fd < fd_list_size && fd_list)
    {
        ret = &fd_list[fd];
    }

    return ret;
}
